package net.lulihu.pangolin.headless.common.container;

import lombok.extern.slf4j.Slf4j;
import net.lulihu.pangolin.headless.common.container.annotation.Component;
import net.lulihu.pangolin.headless.common.container.annotation.ComponentScan;
import org.reflections.Reflections;
import org.reflections.scanners.*;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

/**
 * 扫描器
 * <p>
 * 使用reflections作为类扫描器(注意该jar需要设置镜像地址)
 * <pre>
 *         <dependency>
 *             <groupId>org.reflections</groupId>
 *             <artifactId>reflections</artifactId>
 *             <version>0.9.11</version>
 *         </dependency>
 * </pre>
 */
@Slf4j
public class Scanner {

    /**
     * 扫描组件类
     */
    public static Set<Class<?>> scanningComponentClass(ComponentScan componentScan) {
        if (log.isDebugEnabled()) log.debug("开始扫描ing");

        String[] packageNames = componentScan.value();
        String[] excludePackages = componentScan.excludePackages();

        // 过滤器
        FilterBuilder filter = new FilterBuilder();
        // 扫描包名
        Arrays.stream(packageNames).forEach(filter::includePackage);
        // 排除包名
        Arrays.stream(excludePackages).forEach(filter::excludePackage);

        // 包名所在的资源路径
        Collection<URL> urlTotals = new ArrayList<>();
        for (String packageName : packageNames) {
            Collection<URL> urls = ClasspathHelper.forPackage(packageName);
            if (urls != null && !urls.isEmpty()) urlTotals.addAll(urls);
        }

        Reflections reflections = new Reflections(
            new ConfigurationBuilder()
                .filterInputsBy(filter)
                .setUrls(urlTotals)
                .setScanners(
                    Scanners.SubTypes,
                    Scanners.TypesAnnotated,
                    Scanners.FieldsAnnotated,
                    Scanners.ConstructorsAnnotated,
                    Scanners.MethodsAnnotated,
                    Scanners.MethodsParameter
                )
        );

        return reflections.getTypesAnnotatedWith(Component.class);
    }


}
